package org.baicaixiaozhan.learncommon.util;


import com.google.common.base.Strings;
import org.baicaixiaozhan.learncommon.exception.ScriptException;

import java.util.regex.Pattern;

/**
 * Xss 工具类
 *
 * @since 2021/6/30
 * @author baicaixiaozhan
 */
public class XSSUtils {

    public static final String REPLACE_STRING = " ";
    public static final Pattern SCRIPT_PATTERN_ONE = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    public static final Pattern SCRIPT_PATTERN_TWE = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
    public static final Pattern SCRIPT_PATTERN_THREE = Pattern.compile("<[\r\n| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    public static final Pattern SCRIPT_PATTERN_FOUR = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    public static final Pattern SCRIPT_PATTERN_FIVE = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    public static final Pattern SCRIPT_PATTERN_SIX = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    public static final Pattern SCRIPT_PATTERN_SEVEN = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
    public static final Pattern SCRIPT_PATTERN_EIGHT = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);

    private XSSUtils() {
    }

    /**
     * xss 校验
     *
     * @param str
     * @return
     */
    public static String xssEncode(String str) throws ScriptException {
        if (Strings.isNullOrEmpty(str)) {
            return str;
        } else {
            str = checkXssAndSql(str);
        }
        StringBuilder sb = new StringBuilder(str.length() + 16);
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            switch (c) {
                case '>':
                    // 转义大于号
                    sb.append("＞");
                    break;
                case '<':
                    // 转义小于号
                    sb.append("＜");
                    break;
                case '\'':
                    // 转义单引号
                    sb.append("＇");
                    break;
                case '\"':
                    // 转义双引号
                    sb.append("＂");
                    break;
                case '&':
                    // 转义&
                    sb.append("＆");
                    break;
                case '#':
                    // 转义#
                    sb.append("＃");
                    break;
                default:
                    sb.append(c);
                    break;
            }
        }
        return sb.toString();
    }

    /**
     * xss 校验
     *
     * @param value 目标 str
     * @throws ScriptException 脚本异常
     */
    public static String checkXssAndSql(String value) {
        if (!Strings.isNullOrEmpty(value)) {
            // 避免空字符
            value = value.replaceAll(" ", REPLACE_STRING);
            // 避免脚本标签之间的任何内容
            if (SCRIPT_PATTERN_ONE.matcher(value).find()){
                throw new ScriptException();
            }
            // Remove any lonesome </script> tag
            if (SCRIPT_PATTERN_TWE.matcher(value).find()){
                throw new ScriptException();
            }
            // Remove any lonesome <script ...> tag
            if (SCRIPT_PATTERN_THREE.matcher(value).find()){
                throw new ScriptException();
            }
            // Avoid eval(...) expressions
            if (SCRIPT_PATTERN_FOUR.matcher(value).find()){
                throw new ScriptException();
            }
            // Avoid e-xpression(...) expressions
            if (SCRIPT_PATTERN_FIVE.matcher(value).find()){
                throw new ScriptException();
            }
            // Avoid javascript:... expressions
            if (SCRIPT_PATTERN_SIX.matcher(value).find()){
                throw new ScriptException();
            }
            // Avoid vbscript:... expressions
            if (SCRIPT_PATTERN_SEVEN.matcher(value).find()){
                throw new ScriptException();
            }
            // Avoid onload= expressions
            if (SCRIPT_PATTERN_EIGHT.matcher(value).find()){
                throw new ScriptException();
            }
        }
        return value;
    }

}